package fr.umlv.cmis.listview;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.ResourceBundle;

import javax.swing.ListModel;
import javax.swing.SwingUtilities;
import javax.swing.event.EventListenerList;
import javax.swing.event.ListDataEvent;
import javax.swing.event.ListDataListener;
import javax.swing.event.TableModelEvent;
import javax.swing.filechooser.FileSystemView;
import javax.swing.table.AbstractTableModel;

import kiyut.ekspos.BrowserWindow;

import org.apache.chemistry.opencmis.client.api.CmisObject;
import org.apache.chemistry.opencmis.client.api.Document;
import org.apache.chemistry.opencmis.client.api.Folder;
import org.apache.chemistry.opencmis.client.api.ItemIterable;
import org.apache.chemistry.opencmis.client.api.Property;
import org.apache.chemistry.opencmis.commons.PropertyIds;
import org.apache.chemistry.opencmis.commons.SessionParameter;
import org.apache.chemistry.opencmis.commons.enums.BaseTypeId;
import org.apache.chemistry.opencmis.commons.exceptions.CmisBaseException;

import fr.umlv.authentication.SessionHandler;
import fr.umlv.model.ClientModel;
import fr.umlv.model.ClientSession;
import fr.umlv.model.MIMETypes;
import fr.umlv.utils.ExceptionUtils;

// TODO: Auto-generated Javadoc
/**
 * The Class CmisObjectListViewModel.
 */
public class CmisObjectListViewModel extends AbstractTableModel implements
ListModel, Serializable {
	
	/** The Constant serialVersionUID. */
	private static final long serialVersionUID = 1L;

	/** The Constant ALL_INDEX. */
	public static final int ALL_INDEX = TableModelEvent.HEADER_ROW;

	/** The Constant NAME. */
	public static final int NAME = 0;

	/** The Constant SIZE. */
	public static final int SIZE = 1;

	/** The Constant TYPE. */
	public static final int TYPE = 2;

	/** The Constant MODIFIED. */
	public static final int MODIFIED = 3;

	/** The Constant BROWSE. */
	public static final int BROWSE = 0;

	/** The Constant EDIT. */
	public static final int EDIT = 1;

	/** The Constant DELETE. */
	public static final int DELETE = 2;

	/** The column names. */
	protected String[] columnNames = { "Name", "Size", "Type", "Modified" };

	/** The data. */
	protected List<CmisObject> data;

	/** The list data listeners. */
	protected EventListenerList listDataListeners;

	/** The fsv. */
	protected FileSystemView fsv;

	/** The use file hiding. */
	protected boolean useFileHiding;

	/** The sort by. */
	protected int sortBy;

	/** The state. */
	protected int state;

	/** The directory. */
	protected CmisObject directory;

	/** The model. */
	private ClientModel model;

	/** The bundle. */
	private ResourceBundle bundle = ResourceBundle.getBundle("kiyut.ekspos.BrowserWindow",SessionHandler.getLocale());

	//private final String userName = SessionHandler.getClientSession().getSessionParameters().get(SessionParameter.USER);
	/** The Constant CMIS_USER_HOME. */
	public final static String CMIS_USER_HOME = "/Espaces Utilisateurs/"+SessionHandler.getClientSession().getSessionParameters().get(SessionParameter.USER);


	/**
	 * Instantiates a new cmis object list view model.
	 *
	 * @param useFileHiding the use file hiding
	 * @param sortBy the sort by
	 */
	public CmisObjectListViewModel(boolean useFileHiding, int sortBy) {

		model = new ClientModel();

		ClientSession clientSession = SessionHandler.getClientSession();

		model.setClientSession(clientSession);

		try {
			model.loadFolder(clientSession.getSession().getRepositoryInfo()
					.getRootFolderId(), false);
			//model.loadObject(clientSession.getSession().getRepositoryInfo().getRootFolderId());
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		//this.directory = clientSession.getSession().getRootFolder();
		this.directory = clientSession.getSession().getObjectByPath(CMIS_USER_HOME);
		this.useFileHiding = useFileHiding;
		this.sortBy = sortBy;
		listDataListeners = new EventListenerList();

		data = Collections.synchronizedList(new ArrayList<CmisObject>());


		columnNames = new String[4];
		columnNames[0] = bundle.getString("DetailView.NameColumn");
		columnNames[1] = bundle.getString("DetailView.SizeColumn");
		columnNames[2] = bundle.getString("DetailView.TypeColumn");  
		columnNames[3] = bundle.getString("DetailView.LastModificationColumn");



	}

	/* (non-Javadoc)
	 * @see javax.swing.ListModel#getSize()
	 */
	public int getSize() {
		return getRowCount();
	}

	/* (non-Javadoc)
	 * @see javax.swing.table.TableModel#getColumnCount()
	 */
	public int getColumnCount() {
		return columnNames.length;
	}

	/* (non-Javadoc)
	 * @see javax.swing.table.AbstractTableModel#getColumnName(int)
	 */
	@Override
	public String getColumnName(int col) {
		return columnNames[col];
	}

	/* (non-Javadoc)
	 * @see javax.swing.table.TableModel#getRowCount()
	 */
	public int getRowCount() {
		return data.size();
	}

	/* (non-Javadoc)
	 * @see javax.swing.table.TableModel#getValueAt(int, int)
	 */
	public Object getValueAt(int row, int col) {
		Object object = null;
		CmisObject file = data.get(row);


		switch (col) {
		case NAME:
			object = file;
			break;
		case SIZE:
			object = null;
			if (BaseTypeId.CMIS_DOCUMENT.equals(file.getBaseTypeId())) {
				return ((Document) file).getContentStreamLength();
			}
			break;
		case TYPE:
			String str = file.getType().getDisplayName();
	
			if (BaseTypeId.CMIS_DOCUMENT.equals(file.getBaseTypeId())) {
				str = getExtension(file);		
			}
			
			if (str == null) {
				str = "";
			}
			
			object = str;
			break;
		case MODIFIED:
			Date date = new Date();
			date.setTime(file.getLastModificationDate().getTimeInMillis());
			object = date;
			break;
		}
		
		return object;
	}

	/* (non-Javadoc)
	 * @see javax.swing.ListModel#getElementAt(int)
	 */
	public Object getElementAt(int index) {
		return getValueAt(index, 0);
	}

	/**
	 * Gets the file.
	 *
	 * @param index the index
	 * @return the file
	 */
	public CmisObject getFile(int index) {
		return (CmisObject) getValueAt(index, 0);
	}

	/**
	 * Refresh.
	 */
	public synchronized void refresh() {
		
		new Thread() {
			public void run() {
				SwingUtilities.invokeLater(new Runnable() {
					
					@Override
					public void run() {
						BrowserWindow.myStatusProgressBar.startTransfer();
					}
				});
				data.clear();
				// Group the folder & file
				List<Folder> dirs = new ArrayList<Folder>();
				List<Document> files = new ArrayList<Document>();

				Folder folder = (Folder) directory;

				ItemIterable<CmisObject> iterable = folder.getChildren();
				Iterator<CmisObject> iterator = iterable.iterator();

				String curDir = ((Folder)getCurrentDirectory()).getPath()+"/";
				if(curDir.equals("//")) {
					curDir="/";
				}
				while (iterator.hasNext()) {
					CmisObject cmisobj = iterator.next();

					if (BaseTypeId.CMIS_DOCUMENT.equals(cmisobj.getBaseTypeId())) {
						if(!BrowserWindow.getBlacklist().contains(curDir+cmisobj.getName())) {
							files.add(((Document) cmisobj));
						}
					} else if (BaseTypeId.CMIS_FOLDER.equals(cmisobj.getBaseTypeId())) {
						if(!cmisobj.getName().equals(".gedconfig")) {
							if(!BrowserWindow.getBlacklist().contains(((Folder)cmisobj).getPath())) {
								dirs.add(((Folder) cmisobj));
				
							}
						}
					}
				}

				switch (sortBy) {
				case NAME:
					Collections.sort(dirs,new CmisObjectNameComparator());
					Collections.sort(files,new CmisObjectNameComparator());
					break;
				case SIZE:
					Collections.sort(dirs,new CmisObjectSizeComparator());
					Collections.sort(files,new CmisObjectSizeComparator());
					break;
				case TYPE:
					Collections.sort(dirs,new CmisObjecTypeComparator());
					Collections.sort(files,new CmisObjecTypeComparator());
					break;
				case MODIFIED:
					Collections.sort(dirs,new CmisObjectModifiedComparator());
					Collections.sort(files,new CmisObjectModifiedComparator());
					break;
				}

				data.addAll(dirs);
				data.addAll(files);

				SwingUtilities.invokeLater(new Runnable() {
					
					@Override
					public void run() {
						fireTableDataChanged();
						fireContentsChanged(ALL_INDEX, ALL_INDEX);
						BrowserWindow.myStatusProgressBar.endTransfer();
					}
				});
			};
		}.start();
	}

	/**
	 * Rename file.
	 *
	 * @param index the index
	 * @param filename the filename
	 * @return true, if successful
	 * @throws SecurityException the security exception
	 */
	public synchronized boolean renameFile(final int index, final String filename)
	throws SecurityException {
		new Thread() {
			public void run() {
				SwingUtilities.invokeLater(new Runnable() {
					
					@Override
					public void run() {
						BrowserWindow.myStatusProgressBar.startTransfer();
					}
				});
				try {
					CmisObject file = getFile(index);
			
					Map<String,String> modifyFolderProps = new HashMap<String, String>();
			
					for(Property<?> p :file.getProperties()){
						modifyFolderProps.put(p.getId(),p.getValueAsString());
					}
			
					modifyFolderProps.put(PropertyIds.NAME,filename);
					file.updateProperties(modifyFolderProps,true);
				} catch (CmisBaseException cbe) {
					ExceptionUtils.createCmisJXBaseException(cbe, null);
				}

				SwingUtilities.invokeLater(new Runnable() {
					
					@Override
					public void run() {
						fireTableRowsUpdated(index,index); 
						fireContentsChanged(index,index);
						BrowserWindow.myStatusProgressBar.endTransfer();
					}
				});
			};
		}.start();
		return true;
	}

	/**
	 * Removes the.
	 *
	 * @param index the index
	 * @return the object
	 */
	public Object remove(int index) {
		Object object = data.remove(index);
		fireTableRowsDeleted(index, index);
		fireIntervalRemoved(index, index);
		return object;
	}

	/**
	 * Index of.
	 *
	 * @param elem the elem
	 * @return the int
	 */
	public int indexOf(Object elem) {
		return data.indexOf(elem);
	}

	/**
	 * Checks if is traversable.
	 *
	 * @param index the index
	 * @return true, if is traversable
	 */
	public boolean isTraversable(int index) {
		CmisObject obj = getFile(index);

		if (BaseTypeId.CMIS_FOLDER.equals(obj.getBaseTypeId())) {
			return true;
		}

		return false;
	}

	/**
	 * Checks if is file hiding enabled.
	 *
	 * @return true, if is file hiding enabled
	 */
	public boolean isFileHidingEnabled() {
		return this.useFileHiding;
	}

	/**
	 * Sets the file hiding enabled.
	 *
	 * @param useFileHiding the new file hiding enabled
	 */
	public void setFileHidingEnabled(boolean useFileHiding) {
		this.useFileHiding = useFileHiding;
	}

	/**
	 * Gets the sort by.
	 *
	 * @return the sort by
	 */
	public int getSortBy() {
		return this.sortBy;
	}

	/**
	 * Sets the sort by.
	 *
	 * @param sortBy the new sort by
	 */
	public void setSortBy(int sortBy) {
		this.sortBy = sortBy;
	}

	/**
	 * Gets the current directory.
	 *
	 * @return the current directory
	 */
	public CmisObject getCurrentDirectory() {

		return this.directory;
	}

	/**
	 * Sets the current directory.
	 *
	 * @param directory the new current directory
	 */
	public void setCurrentDirectory(CmisObject directory) {

		if (directory == null) {
			// this.directory = fsv.getHomeDirectory();
		} else {
			this.directory = directory;
		}
	}

	/**
	 * Gets the file system view.
	 *
	 * @return the file system view
	 */
	public FileSystemView getFileSystemView() {
		return fsv;
	}

	/**
	 * Sets the file system view.
	 *
	 * @param fsv the new file system view
	 */
	public void setFileSystemView(FileSystemView fsv) {
		this.fsv = fsv;
	}

	/**
	 * Gets the state.
	 *
	 * @return the state
	 */
	public int getState() {
		return this.state;
	}

	/**
	 * Sets the state.
	 *
	 * @param state the new state
	 */
	public void setState(int state) {
		this.state = state;
	}

	/* (non-Javadoc)
	 * @see javax.swing.ListModel#addListDataListener(javax.swing.event.ListDataListener)
	 */
	public void addListDataListener(ListDataListener l) {
		listDataListeners.add(ListDataListener.class, l);
	}

	/* (non-Javadoc)
	 * @see javax.swing.ListModel#removeListDataListener(javax.swing.event.ListDataListener)
	 */
	public void removeListDataListener(ListDataListener l) {
		listDataListeners.remove(ListDataListener.class, l);
	}

	/**
	 * Fire contents changed.
	 *
	 * @param index0 the index0
	 * @param index1 the index1
	 */
	public void fireContentsChanged(int index0, int index1) {
		Object[] listeners = listDataListeners.getListenerList();
		ListDataEvent e = null;

		for (int i = listeners.length - 2; i >= 0; i -= 2) {
			if (listeners[i] == ListDataListener.class) {
				if (e == null) {
					e = new ListDataEvent(this, ListDataEvent.CONTENTS_CHANGED,
							index0, index1);
				}
				((ListDataListener) listeners[i + 1]).contentsChanged(e);
			}
		}
	}

	/**
	 * Fire interval added.
	 *
	 * @param index0 the index0
	 * @param index1 the index1
	 */
	public void fireIntervalAdded(int index0, int index1) {
		Object[] listeners = listDataListeners.getListenerList();
		ListDataEvent e = null;

		for (int i = listeners.length - 2; i >= 0; i -= 2) {
			if (listeners[i] == ListDataListener.class) {
				if (e == null) {
					e = new ListDataEvent(this, ListDataEvent.INTERVAL_ADDED,
							index0, index1);
				}
				((ListDataListener) listeners[i + 1]).intervalAdded(e);
			}
		}
	}

	/**
	 * Fire interval removed.
	 *
	 * @param index0 the index0
	 * @param index1 the index1
	 */
	public void fireIntervalRemoved(int index0, int index1) {
		Object[] listeners = listDataListeners.getListenerList();
		ListDataEvent e = null;

		for (int i = listeners.length - 2; i >= 0; i -= 2) {
			if (listeners[i] == ListDataListener.class) {
				if (e == null) {
					e = new ListDataEvent(this, ListDataEvent.INTERVAL_REMOVED,
							index0, index1);
				}
				((ListDataListener) listeners[i + 1]).intervalRemoved(e);
			}
		}
	}

	/**
	 * The Class CmisObjectNameComparator.
	 */
	private class CmisObjectNameComparator implements Comparator<CmisObject> {
		
		/* (non-Javadoc)
		 * @see java.util.Comparator#compare(java.lang.Object, java.lang.Object)
		 */
		public int compare(CmisObject file1, CmisObject file2) {
			return file1.getName().toLowerCase().compareTo(file2.getName().toLowerCase());
		}
	}

	/**
	 * The Class CmisObjectSizeComparator.
	 */
	private class CmisObjectSizeComparator implements Comparator<CmisObject> {
		
		/* (non-Javadoc)
		 * @see java.util.Comparator#compare(java.lang.Object, java.lang.Object)
		 */
		public int compare(CmisObject file1, CmisObject file2) {
			int comp;

			if (getSize(file1) < getSize(file2)) {
				comp = -1;
			} else if (getSize(file1) == getSize(file2)) {
				comp = file1.getName().toLowerCase().compareTo(file2.getName().toLowerCase());
			} else {
				comp = 1;
			}

			return comp;
		}
	}

	/**
	 * The Class CmisObjecTypeComparator.
	 */
	private class CmisObjecTypeComparator implements Comparator<CmisObject> {
		
		/* (non-Javadoc)
		 * @see java.util.Comparator#compare(java.lang.Object, java.lang.Object)
		 */
		public int compare(CmisObject file1, CmisObject file2) {
			int comp;
			
			String desc1 = getExtension(file1);
			String desc2 = getExtension(file2);
			
			if (desc1 == null) {
				desc1 = "";
			}
			if (desc2 == null) {
				desc2 = "";
			}

			comp = desc1.compareTo(desc2);
			if (comp == 0) {
				comp = file1.getName().toLowerCase().compareTo(file2.getName().toLowerCase());
			}

			return comp;
		}

	}

	/**
	 * The Class CmisObjectModifiedComparator.
	 */
	private class CmisObjectModifiedComparator implements Comparator<CmisObject> {
		
		/* (non-Javadoc)
		 * @see java.util.Comparator#compare(java.lang.Object, java.lang.Object)
		 */
		public int compare(CmisObject file1, CmisObject file2) {
			int comp;

			if (file1.getLastModificationDate().getTimeInMillis() < file2.getLastModificationDate().getTimeInMillis()) {
				comp = -1;
			} else if (file1.getLastModificationDate().getTimeInMillis() == file2.getLastModificationDate().getTimeInMillis()) {
				comp = file1.getName().toLowerCase().compareTo(file2.getName().toLowerCase());
			} else {
				comp = 1;
			}

			return comp;
		}
	}

	/**
	 * Gets the model.
	 *
	 * @return the model
	 */
	public ClientModel getModel() {
		return model;
	}

	/**
	 * Gets the size.
	 *
	 * @param file the file
	 * @return the size
	 */
	public long getSize(CmisObject file){
		if (BaseTypeId.CMIS_DOCUMENT.equals(file.getBaseTypeId())) {
			return ((Document) file).getContentStreamLength();
		}
		return 0;
	}

	private String getExtension(CmisObject file){
		
		String str = file.getType().getDisplayName();
		
		if (BaseTypeId.CMIS_DOCUMENT.equals(file.getBaseTypeId())) {
			str = MIMETypes.getExtension(((Document) file).getContentStreamMimeType(),"");
			if(str==null || str.isEmpty()){
				
				str = ((Document) file).getContentStreamMimeType();
				str = str.substring(str.lastIndexOf('/')+1);
			}
			
		}
		
		return str;
		
	}
}

